package org.zzrblog.blogapp.utils;

import android.opengl.GLES20;
import android.util.Log;

import static android.opengl.GLES20.GL_COMPILE_STATUS;
import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
import static android.opengl.GLES20.GL_LINK_STATUS;
import static android.opengl.GLES20.GL_VALIDATE_STATUS;
import static android.opengl.GLES20.GL_VERTEX_SHADER;
import static android.opengl.GLES20.glAttachShader;
import static android.opengl.GLES20.glCompileShader;
import static android.opengl.GLES20.glCreateProgram;
import static android.opengl.GLES20.glCreateShader;
import static android.opengl.GLES20.glDeleteProgram;
import static android.opengl.GLES20.glDeleteShader;
import static android.opengl.GLES20.glGetError;
import static android.opengl.GLES20.glGetProgramInfoLog;
import static android.opengl.GLES20.glGetProgramiv;
import static android.opengl.GLES20.glGetShaderInfoLog;
import static android.opengl.GLES20.glGetShaderiv;
import static android.opengl.GLES20.glLinkProgram;
import static android.opengl.GLES20.glShaderSource;
import static android.opengl.GLES20.glValidateProgram;

/**
 * Created by ZZR on 2017/2/7.
 */

public class ShaderHelper {
    private static final String TAG = "ShaderHelper";

    public static int compileVertexShader(String shaderCode){
        return compileShader(GL_VERTEX_SHADER, shaderCode);
    }

    public static int compileFragmentShader(String shaderCode){
        return compileShader(GL_FRAGMENT_SHADER, shaderCode);
    }

    private static int compileShader(int type, String shaderCode){
        final int shaderObjectId = glCreateShader(type);
        if (shaderObjectId == 0){
            if(LoggerConfig.ON){
                Log.w(TAG," Warning! Could not create new shader, glGetError:"+glGetError());
            }
            return 0;
        }
        glShaderSource(shaderObjectId, shaderCode);

        glCompileShader(shaderObjectId);

        final int[] compileStatus = new int[1];
        glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS, compileStatus, 0);

        if(LoggerConfig.ON){
            Log.i(TAG, "Result of compiling source:"+"\n"+shaderCode+"\n"
                    + glGetShaderInfoLog(shaderObjectId));
        }

        if(compileStatus[0] == 0){
            glDeleteShader(shaderObjectId);
            if(LoggerConfig.ON){
                Log.w(TAG," Warning! Compilation of shader failed, glGetError:"+glGetError());
            }
            return 0;
        }

        return shaderObjectId;
    }


    public static int linkProgram(int vertexShaderId, int fragmentShaderId){
        final int programObjectId = glCreateProgram();
        if (programObjectId == 0){
            if(LoggerConfig.ON){
                Log.w(TAG," Warning! Could not create new program, glGetError:"+glGetError());
            }
            return 0;
        }

        glAttachShader(programObjectId, vertexShaderId);
        glAttachShader(programObjectId, fragmentShaderId);

        glLinkProgram(programObjectId);

        final int[] linkStatus = new int[1];
        glGetProgramiv(programObjectId, GL_LINK_STATUS, linkStatus, 0);

        if(LoggerConfig.ON){
            Log.i(TAG, "Result of linking program:"
                    + glGetProgramInfoLog(programObjectId));
        }

        if(linkStatus[0] == 0){
            glDeleteProgram(programObjectId);
            if(LoggerConfig.ON){
                Log.w(TAG," Warning! Linking of program failed, glGetError:"+glGetError());
            }
            return 0;
        }

        return programObjectId;
    }


    public static boolean validateProgram(int programObjectId){
        glValidateProgram(programObjectId);

        final int[] validateStatus = new int[1];
        glGetProgramiv(programObjectId, GL_VALIDATE_STATUS, validateStatus, 0);

        if(LoggerConfig.ON){
            Log.i(TAG, "Result of validating program:"+validateStatus[0]
                    +"\nLog:"+ glGetProgramInfoLog(programObjectId));
        }

        return validateStatus[0]!= GLES20.GL_FALSE ;
    }


    public static int buildProgram(String vertexShaderSource, String fragmentShaderSource){
        int programObjectId;

        int vertexShader = compileVertexShader(vertexShaderSource);
        int fragmentShader = compileFragmentShader(fragmentShaderSource);

        programObjectId = linkProgram(vertexShader,fragmentShader);

        if(LoggerConfig.ON){
            validateProgram(programObjectId);
        }

        return programObjectId;
    }
}
